cargo new rift-replacer && mv $_ __rift-replacer && cd $_ && cargo add clap@4.1.4 --features derive && cat > src/main.rs <<EOF
use clap::{Parser, Subcommand};
use std::io::{BufRead, Write};
#[derive(Parser, Debug)]
#[command(author="Neutron3529<qweytr1@163.com>", version="0.1.0", about="Very simple program that provide batch modification for mainly Riftbreaker", long_about = None)]
struct Args {
    /// dir to execute
    #[arg(short, long, default_value = ".")]
    dir: String,
    /// pattern to trigger the modification
    #[arg(short, long)]
    trigger: String,
    /// pattern to apply the modification
    #[arg(short, long)]
    modification: String,
    /// filter of the file name, the processed file must contains it as the file name
    #[arg(short, long, default_value = ".")]
    filter: String,
    /// recurse into directories
    #[arg(short, long, default_value_t = false)]
    recurse: bool,
    /// working_depth, optional. if set, only matching the working depth is allowed.
    #[arg(short, long, default_value_t = -1)]
    working_depth: i32,
    //    /// current depth, defaults to -1, which means the match is not triggered. Should not modify it in most of cases.
    //    #[arg(short,long, default_value_t=-1)]
    //    cdepth: i32,
    #[command(subcommand)]
    command: Command,
}
#[derive(Subcommand, Debug)]
enum Command {
    /// scaling the modification
    Scale {
        /// scale of the modification
        scale: f64,
    },
    /// overwrite the old value to this new value
    Overwrite {
        /// scale of the modification
        overwrite: f64,
    },
    /// replace content when trigger the replace command
    Replace {
        /// content that we need
        content: String,
    },
}
fn main() {
    let mut args = Args::parse();
    // println!("{:?}", args);
    let dir = std::mem::take(&mut args.dir);
    if let Command::Replace { ref mut content } = &mut args.command {
        *content = content
            .replace("\\\\\\\\", "\\\\")
            .replace("\\\\n", "\\n")
            .replace("\\\\t", "\\t")
    }
    for i in walk(&args, dir.clone(), dir.as_str()).unwrap() {
        println!("Skip \\x1b[1;31m{}\\x1b[m", i)
    }
}
fn walk<P: AsRef<std::path::Path>>(
    args: &Args,
    dir: P,
    root: &str,
) -> std::io::Result<Vec<String>> {
    let mut ret = Vec::new();
    for entry in std::fs::read_dir(&dir)? {
        //        let entry = entry?;
        let path = entry?.path();
        if path.is_dir() {
            if args.recurse {
                ret.append(&mut walk(args, path, root)?);
            } else {
                eprintln!("{:?} is a dir, not a file!", path)
            }
        } else if path
            .file_name()
            .map(|x| {
                x.to_str()
                    .unwrap_or_else(|| {
                        eprintln!("{:?} cannot converted into str", path.file_name());
                        &""
                    })
                    .contains(&args.filter)
            })
            .unwrap_or_else(|| {
                eprintln!("{:?} cannot converted into str", path.file_name());
                false
            })
        {
            process(&args, path, root)?;
        } else {
            ret.push(
                path.as_os_str()
                    .to_str()
                    .unwrap_or("UNKNOWN_FILE")
                    .replace(root, ""),
            )
        }
    }
    Ok(ret)
}
fn process(args: &Args, path: std::path::PathBuf, root: &str) -> std::io::Result<()> {
    let mut file = std::io::BufReader::new(std::fs::File::open(&path)?);
    let mut content = String::new();
    let mut depth = -1;
    for (lno, mut l) in std::iter::from_fn(move || {
        let mut buf=Vec::new();
        match file.read_until(b'\\n',&mut buf) {
            Ok(x) if x > 0 => {
                return Some(String::from_utf8(buf).unwrap())
            }
            _=>None
        }

    }).enumerate() {
        if depth == -1
            && l.split_whitespace()
                //split(char::is_whitespace)
                //.filter(|x| x.len() > 0)
                .any(|x| x == args.trigger)
        {
            depth = 0;
        }
        if depth >= 0 {
            let delta = l
                .chars()
                .fold(0, |s, x| s + (x == '{') as i32 - (x == '}') as i32);
            if delta != 0 {
                depth += delta;
                if depth == 0 {
                    depth = -1
                }
            }
        }
        if (depth > 0 && args.working_depth == -1) || depth == args.working_depth {
            if l.split_whitespace().any(|x| x == args.modification) {
                l = match &args.command {
                    Command::Scale { scale } => {
                        process_scaling(l, *scale, (&path, lno, depth, root))
                    }

                    Command::Overwrite { overwrite } => {
                        process_overwrite(l, *overwrite, (&path, lno, depth, root))
                    }
                    Command::Replace { content } => {
                        process_replace(l, content, (&path, lno, depth, root))
                    }
                }
            }
        }
        content += &l;
    }
    let mut file = std::fs::File::create(&path)?;
    file.write(content.as_bytes())?;
    Ok(())
}
fn modification<P: AsRef<std::path::Path>>(
    l: String,
    new: String,
    info: (P, usize, i32, &str),
) -> String {
    println!(
        "{}\\x1b[1;32mL{}(d{}):\\x1b[m{}\\x1b[1;32m->\\x1b[m{}",
        // "{}L {}(d{}): {} -> {} ",
        info.0
            .as_ref()
            .as_os_str()
            .to_str()
            .unwrap_or("UNKNOWN_FILE")
            .replace(info.3, ""),
        info.1 + 1,
        info.2,
        l,
        new
    );
    new
}
fn process_scaling<P: AsRef<std::path::Path>>(
    l: String,
    scale: f64,
    info: (P, usize, i32, &str),
) -> String {
    let mut s: Vec<&str> = l.split('"').collect();
    let num = (s[1].parse::<f64>().unwrap() * scale).to_string();
    s[1] = &num;
    let new = s.join(&"\\"");
    modification(l, new, info)
}

fn process_overwrite<P: AsRef<std::path::Path>>(
    l: String,
    overwrite: f64,
    info: (P, usize, i32, &str),
) -> String {
    let mut s: Vec<&str> = l.split('"').collect();
    let num = overwrite.to_string();
    s[1] = &num;
    let new = s.join(&"\\"");
    modification(l, new, info)
}
fn process_replace<P: AsRef<std::path::Path>>(
    l: String,
    content: &str,
    info: (P, usize, i32, &str),
) -> String {
    let new = String::new() + &l + content;
    modification(l, new, info)
}
EOF
cargo build --release && strip -s target/release/rift-replacer && cp target/release/rift-replacer .. && cd .. && rm -r __rift-replacer
